package com.jyniubi.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jyniubi.base.exception.Assert;
import com.jyniubi.base.result.ResponseEnum;
import com.jyniubi.base.utils.JwtUtils;
import com.jyniubi.core.enums.TransTypeEnum;
import com.jyniubi.core.hfb.FormHelper;
import com.jyniubi.core.hfb.HfbConst;
import com.jyniubi.core.hfb.RequestHelper;
import com.jyniubi.core.mapper.UserAccountMapper;
import com.jyniubi.core.pojo.entity.TransFlow;
import com.jyniubi.core.pojo.entity.UserAccount;
import com.jyniubi.core.pojo.entity.UserInfo;
import com.jyniubi.core.service.TransFlowService;
import com.jyniubi.core.service.UserAccountService;
import com.jyniubi.core.service.UserInfoService;
import com.jyniubi.core.utils.LendNoUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 用户账户 服务实现类
 * </p>
 *
 * @author jyzhang
 * @since 2024-04-27
 */
@Service
@Slf4j
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

    @Resource
    private UserInfoService userInfoService;
    @Resource
    private TransFlowService transFlowService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Override
    @Transactional
    public String recharge(String token, BigDecimal amount) {
        //从token中将用户的id解析出来
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = getAndCheckUserInfo(userId);
        //判断金额
        Assert.assertTrue(amount==null || amount.doubleValue()<0,ResponseEnum.RECHARGE_AMOUNT_ERROR);
        Map<String, Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);//hfb中商户的编号
        //生成唯一的充值单号：
        String chargeNo = LendNoUtils.getChargeNo();
        map.put("agentBillNo",chargeNo);//hfb回调时会传入到回调接口中
        map.put("bindCode",userInfo.getBindCode());//充值的会员在hfb中的编号
        map.put("chargeAmt",amount);
        map.put("feeAmt",0);
        map.put("notifyUrl",HfbConst.RECHARGE_NOTIFY_URL);
        map.put("returnUrl",HfbConst.RECHARGE_RETURN_URL);
        map.put("timestamp",System.currentTimeMillis());
        String sign = RequestHelper.getSign(map);
        map.put("sign",sign);
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userId);
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(chargeNo);
        transFlow.setTransType(TransTypeEnum.RECHARGE.getTransType());
        transFlow.setTransTypeName(TransTypeEnum.RECHARGE.getTransTypeName());
        transFlow.setTransAmount(amount);//充值金额 = 用户提交的充值金额+商户手续费
        transFlow.setMemo(TransTypeEnum.RECHARGE.getTransTypeName()+": "+amount.doubleValue()+" 元");
        transFlowService.save(transFlow);
        return FormHelper.buildForm(HfbConst.RECHARGE_URL,map);
    }

    private UserInfo getAndCheckUserInfo(Long userId) {
        //根据id获取用户信息
        UserInfo userInfo = userInfoService.getById(userId);
        //判断用户的状态
        Assert.assertTrue(userInfo.getStatus() != 1, ResponseEnum.LOGIN_LOKED_ERROR);
        return userInfo;
    }

    @Override
    public String rechargeNotify(Map<String, Object> map) {
        // 获取商户充值订单号
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class).eq(TransFlow::getTransNo, agentBillNo));
        // 保证幂等性
        if (transFlow.getStatus()==1) {
            log.error("充值已操作成功：{}",JSON.toJSONString(map));
            return "success";
        }
        //验证充值金额：使用用户提交充值请求的充值金额和 回调hfb传入的充值金额比较
        if (transFlow == null ||
                transFlow.getTransAmount().doubleValue() != new BigDecimal(map.get("chargeAmt").toString()).doubleValue()){
            log.error("充值金额错误 : {}" , JSON.toJSONString(map));
            return "fail";
        }
        //充值成功：更新会员账户
        String bindCode = map.get("bindCode").toString();
        UserInfo userInfo = userInfoService.getOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getBindCode, bindCode));
        Long userInfoId = userInfo.getId();
        UserAccount userAccount
                = this.getOne(Wrappers.lambdaQuery(UserAccount.class).eq(UserAccount::getUserId, userInfoId));
        userAccount.setAmount(userAccount.getAmount().add(new BigDecimal(map.get("chargeAmt").toString())));
        this.updateById(userAccount);
        transFlow.setStatus(1);
        transFlowService.updateById(transFlow);
        //充值成功：发送短信通知用户
        Map<String,Object> params = new HashMap<>();
        params.put("mobile",userInfo.getMobile());
        params.put("type","1");
        params.put("money",map.get("chargeAmt").toString());
        rabbitTemplate.convertAndSend("sms.exchange","sms.acc.recharge",params);
        return "success";
    }

    @Override
    public String withdraw(String token, BigDecimal amount) {
        //从token中将用户的id解析出来
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = getAndCheckUserInfo(userId);
        //校验提现金额必须大于0
        Assert.assertTrue(amount.doubleValue()<=0,ResponseEnum.LEND_WITHDRAW_AMOUNT_ERROR);
        UserAccount userAccount = this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
        //校验提现金额不能小于用户账户总金额
        Assert.assertTrue(amount.doubleValue()>userAccount.getAmount().doubleValue(),ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);
        //构建表单
        //3、构建表单
        Map<String, Object> map = new HashMap<>();
        map.put("agentId",HfbConst.AGENT_ID);
        String withdrawNo = LendNoUtils.getWithdrawNo().toString();
        map.put("agentBillNo",withdrawNo);//提现单号
        map.put("bindCode",userInfo.getBindCode());//提现用户的bindcode
        map.put("fetchAmt",amount);
        // 放款给借款人时扣除手续费， 投资人回款时 从收入中扣除手续费
        map.put("feeAmt","0");
        map.put("returnUrl",HfbConst.WITHDRAW_RETURN_URL);
        map.put("notifyUrl",HfbConst.WITHDRAW_NOTIFY_URL);
        map.put("timestamp",System.currentTimeMillis());
        map.put("sign",RequestHelper.getSign(map));
        //保存提现日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userId);
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(withdrawNo);
        transFlow.setTransType(TransTypeEnum.WITHDRAW.getTransType());
        transFlow.setTransTypeName(TransTypeEnum.WITHDRAW.getTransTypeName());
        transFlow.setTransAmount(amount);//充值金额 = 用户提交的充值金额+商户手续费
        transFlow.setMemo(TransTypeEnum.WITHDRAW.getTransTypeName()+": "+amount.doubleValue()+" 元");
//        transFlow.setStatus();
        transFlowService.save(transFlow);
        return FormHelper.buildForm(HfbConst.WITHDRAW_URL,map);
    }

    @Override
    public String notifyWithdraw(HttpServletRequest request) {
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        boolean signEquals = RequestHelper.isSignEquals(map);
        if (!signEquals){
            log.error("提现回调签名校验失败：{}",JSON.toJSONString(map));
            return "fail";
        }
        if (!"0001".equals(map.get("resultCode"))){
            log.error("提现回调业务失败：{}",JSON.toJSONString(map));
            return "fail";
        }
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class).eq(TransFlow::getTransNo, agentBillNo));
        //校验金额是否和申请的一致
        if (transFlow.getStatus() == 1){
            return "success";
        }
        if (transFlow.getTransAmount().doubleValue() != new BigDecimal(map.get("fetchAmt").toString()).doubleValue()){
            log.error("提现金额不一致：{}",JSON.toJSONString(map));
            return "fail";
        }
        UserAccount userAccount = this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, transFlow.getUserId()));
        userAccount.setAmount(userAccount.getAmount().subtract(transFlow.getTransAmount()));
        this.updateById(userAccount);
        transFlow.setStatus(1);
        transFlowService.updateById(transFlow);
        return "success";
    }
}
